package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.Shipment;
import com.arpa.ntocc.common.service.IShipmentService;
import com.arpa.wms.cache.*;
import com.arpa.wms.domain.consts.CommonConst;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.GoodsDTO;
import com.arpa.wms.domain.dto.GoodsExcelDTO;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.DeleteFlagEnum;
import com.arpa.wms.domain.enums.GoodsTypeEnum;
import com.arpa.wms.domain.enums.StatusEnum;
import com.arpa.wms.domain.enums.StorageStrategyEnum;
import com.arpa.wms.domain.vo.*;
import com.arpa.wms.mapper.GoodsMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qimen.api.request.ItemsSynchronizeRequest;
import com.qimen.api.request.SingleitemSynchronizeRequest;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.arpa.wms.domain.consts.CommonConst.REMARKS_NAME;
import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * @author: Allen chen
 * @Date 2020/8/13
 * @Description:
 */
@Service
@Log4j2
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {

    @Autowired
    private ShipmentCache shipmentCache;
    @Autowired
    private IGoodsUnitConversionService iGoodsUnitConversionService;

    @Autowired
    private IGoodsTypeService iGoodsTypeService;

    @Autowired
    @Lazy
    private IBrandService iBrandService;

    @Autowired
    @Lazy
    private IGoodsUnitService iGoodsUnitService;

    @Autowired
    @Lazy
    private IBatchRuleService iBatchRuleService;

    @Autowired
    private IReceiveItemService iReceiveItemService;

    @Autowired
    @Lazy
    private IInitializationService iInitializationService;

    @Autowired
    private ICountingService iCountingService;

    @Autowired
    private IDifferenceService iDifferenceService;

    @Resource
    private GoodsCache goodsCache;

    @Resource
    private UnitCache unitCache;

    @Resource
    private BrandCache brandCache;

    @Resource
    private BatchRuleCache batchRuleCache;

    @Resource
    private GoodsTypeCache goodsTypeCache;

    @Resource
    private PartyCache partyCache;

    @Autowired
    private IShipmentService shipmentService;

    @Autowired
    private ImportTempCache importTempCache;

    @Autowired
    private GoodsUnitConversionCache goodsUnitConversionCache;

    @Autowired
    private IGoodsPnService goodsPnService;

    @Autowired
    @Lazy
    private IReceiveItemService receiveItemService;

    private static final String GOODS_BAR_MANAGE = "同意所属机构下商品条码不能重复！";
    private static final String GOODS_CODE_MANAGE = "同意所属机构下商品编码不能重复！";

    private final static String DEFAULT = "default, ";
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(Goods entity) {

        entity.setCode(IdUtil.simpleUUID());
        entity.setCreatedBy(UserUtil.getCode());
        entity.setModifiedBy(UserUtil.getCode());
        entity.setPartyGroupCode(UserUtil.getBranchCode());
        String loginName = partyCache.translate(UserUtil.getCode());
        entity.setCreatedName(loginName);
        entity.setModifiedName(loginName);
        entity.setStatus(StatusEnum.UNACTIVE.getValue());
        entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        //同一个货主下商品编号和商品条码都不可以重复
        //基本单位code
        String unitCode = entity.getUnitCode();
        //单位编码和条码 同一个货主下不能重复
        checkGoodsCodeAndBarCode(entity);
        boolean flag = super.save(entity);
        if (flag) {
            BigDecimal oneHundred = new BigDecimal("100");
            BigDecimal oneThousand = new BigDecimal("1000");
            entity.getGoodsUnitConversionList().forEach(r -> {
                r.setCode(IdUtil.simpleUUID());
                r.setGoodsCode(entity.getCode());
                r.setModifiedBy(UserUtil.getCode());
                r.setCreatedBy(UserUtil.getCode());
                r.setModifiedName(loginName);
                r.setCreatedName(loginName);
                r.setGroupCode(UserUtil.getBranchCode());
                //判断基本单位数据
                if(r.getUnitCode().equals(unitCode)){
                    r.setFlag(1);
                }else {
                    r.setFlag(0);
                }
                //将长、宽、高装换为cm，体积装换为L；
                if (null != r.getLength() && r.getLength().compareTo(BigDecimal.ZERO) > 0) {
                    r.setLength(r.getLength().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getWidth() && r.getWidth().compareTo(BigDecimal.ZERO) > 0) {
                    r.setWidth(r.getWidth().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getHeight() && r.getHeight().compareTo(BigDecimal.ZERO) > 0) {
                    r.setHeight(r.getHeight().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getVolume() && r.getVolume().compareTo(BigDecimal.ZERO) > 0) {
                    r.setVolume(r.getVolume().multiply(oneThousand).setScale(6, BigDecimal.ROUND_HALF_UP));
                }
            });
            iGoodsUnitConversionService.saveBatch(entity.getGoodsUnitConversionList());
        }
        if(StrUtil.isNotBlank(entity.getBarCode())) {
            GoodsPn pn = new GoodsPn();
            pn.setCode(IdUtil.fastSimpleUUID());
            pn.setGoodsCode(entity.getCode());
            pn.setGoodCode(entity.getGoodCode());
            pn.setPn(entity.getBarCode());
            pn.setGoodsName(entity.getName());
            pn.setGroupCode(UserUtil.getBranchCode());
            pn.setShipmentCode(entity.getShipmentCode());
            pn.setShipmentName(entity.getShipmentName());
            pn.setModifiedBy(UserUtil.getCode());
            pn.setCreatedBy(UserUtil.getCode());
            pn.setModifiedName(loginName);
            pn.setCreatedName(loginName);
            goodsPnService.save(pn);
        }
        goodsCache.flushAllCacheByGroupCode(UserUtil.getBranchCode());
        goodsCache.flushAllCacheByShipmentCode(entity.getShipmentCode());
        return flag;
    }

    public void checkGoodsCodeAndBarCode(Goods dto){
        GoodsDTO query = new GoodsDTO();
        query.setBarCode(dto.getBarCode());
        query.setShipmentCode(dto.getShipmentCode());
        query.setPartyGroupCode(UserUtil.getBranchCode());
        List<GoodsVO> goodsVOS = this.queryByCondition(query);
        if(IterUtil.isNotEmpty(goodsVOS)){
            throw new ServiceException(GOODS_BAR_MANAGE);
        }
        query.setBarCode("");
        query.setGoodCode(dto.getGoodCode());
        List<GoodsVO> goods = this.queryByCondition(query);
        if(IterUtil.isNotEmpty(goods)){
            throw new ServiceException(GOODS_CODE_MANAGE);
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Goods entity) {
        String groupCode = UserUtil.getBranchCode();
        //修改商品编码或者商品条码时，有入库单生成就不准修改
        Goods goods = goodsCache.getObj(entity.getCode());
        if (!goods.getGoodCode().equals(entity.getGoodCode())) {
            int count = receiveItemService.count(new LambdaQueryWrapper<ReceiveItem>()
                    .eq(ReceiveItem::getGoodCode, goods.getGoodCode())
                    .eq(ReceiveItem::getGroupCode, groupCode));
            if (count > 0) {
                throw new ServiceException("商品编码【" + entity.getGoodCode() +"】已生成收货单，无法修改");
            }

        }
        if (!goods.getBarCode().equals(entity.getBarCode())) {
            int count = receiveItemService.count(new LambdaQueryWrapper<ReceiveItem>()
                    .eq(ReceiveItem::getGoodsBarCode, goods.getBarCode())
                    .eq(ReceiveItem::getGroupCode, groupCode));
            if (count > 0) {
                throw new ServiceException("商品条码【" + entity.getBarCode() +"】已生成收货单，无法修改");
            }
        }

        entity.setPartyGroupCode(groupCode);
        if (StrUtil.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        iGoodsUnitConversionService.remove(new QueryWrapper<GoodsUnitConversion>().lambda().eq(GoodsUnitConversion::getGoodsCode, entity.getCode()).eq(GoodsUnitConversion::getGroupCode,groupCode));
        if (IterUtil.isNotEmpty(entity.getGoodsUnitConversionList())) {
            BigDecimal oneHundred = new BigDecimal("100");
            BigDecimal oneThousand = new BigDecimal("1000");
            entity.getGoodsUnitConversionList().forEach(r -> {
                r.setCode(IdUtil.simpleUUID());
                r.setGoodsCode(entity.getCode());
                r.setModifiedName(partyCache.translate(UserUtil.getCode()));
                r.setModifiedBy(UserUtil.getCode());
                r.setCreatedName(partyCache.translate(UserUtil.getCode()));
                r.setCreatedBy(UserUtil.getCode());
                //将长、宽、高装换为cm，体积装换为L；
                if (null != r.getLength() && r.getLength().compareTo(BigDecimal.ZERO) > 0) {
                    r.setLength(r.getLength().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getWidth() && r.getWidth().compareTo(BigDecimal.ZERO) > 0) {
                    r.setWidth(r.getWidth().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getHeight() && r.getHeight().compareTo(BigDecimal.ZERO) > 0) {
                    r.setHeight(r.getHeight().multiply(oneHundred).setScale(3, BigDecimal.ROUND_HALF_UP));
                }
                if (null != r.getVolume() && r.getVolume().compareTo(BigDecimal.ZERO) > 0) {
                    r.setVolume(r.getVolume().multiply(oneThousand).setScale(6, BigDecimal.ROUND_HALF_UP));
                }
            });
            iGoodsUnitConversionService.saveBatch(entity.getGoodsUnitConversionList());
        }
        goodsCache.flushCache(entity.getCode());
        return baseMapper.update(entity, new QueryWrapper<Goods>().lambda().eq(Goods::getCode, entity.getCode()).eq(Goods::getPartyGroupCode,groupCode));
    }

    @Override
    public List<GoodsVO> queryByBrandCode(List<String> codes) {
        return baseMapper.queryByBrandCode(codes,UserUtil.getBranchCode());
    }

    @Override
    public List<GoodsVO> queryByCondition(GoodsDTO goodsDTO) {

        return baseMapper.queryByCondition(goodsDTO);
    }

    @Override
    public List<GoodsVO> queryByCodes(List<String> codes) {
        return baseMapper.queryByCodes(codes,UserUtil.getBranchCode());
    }

    @Override
    public GoodsVO detailWithUnitConversion(GoodsDTO brandDTO) {

        Goods one = getOne(new QueryWrapper<Goods>().lambda().eq(Goods::getCode, brandDTO.getCode()).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
        if (null == one) {
            return null;
        }
        GoodsVO vo = new GoodsVO();
        BeanUtils.copyProperties(one, vo);
        vo.setUnitName(unitCache.translate(one.getUnitCode()));
        vo.setBrandText(brandCache.translate(one.getBrandCode()));
        vo.setTypeText(goodsTypeCache.translate(one.getTypeCode()));
        StatusEnum enumByValue = StatusEnum.getEnumByValue(one.getStatus());
        if(null != enumByValue){
            vo.setStatusText(enumByValue.getDesc());
        }
        StorageStrategyEnum enumByCode = StorageStrategyEnum.getEnumByValue(one.getStorageStrategy());
        if (null != enumByCode) {
            vo.setStorageStrategyText(enumByCode.getDesc());
        }
        List<String> goods = new ArrayList<>();
        goods.add(one.getCode());
        List<GoodsUnitConversionVO> unitConversionVOList = iGoodsUnitConversionService.queryListByGoodsCodes(goods);
        if (null != unitConversionVOList && unitConversionVOList.size() > 0) {
            vo.setUnits(unitConversionVOList);
        }
        return vo;
    }

    @Override
    public List<GoodsVO> query(GoodsDTO goodsDTO) {
        List<GoodsVO> query = baseMapper.query(goodsDTO);
        if (null == query || query.size() <= 0) {
            return new ArrayList<GoodsVO>();
        }
        List<String> unitCodes = new ArrayList<>();
        List<String> goods = new ArrayList<>();
        query.forEach(info -> {
            unitCodes.add(info.getUnitCode());
            goods.add(info.getCode());
        });
        //查询单位
        List<UnitVO> unitVoS = iGoodsUnitService.queryUnitsByCodes(unitCodes);
        Map<String, UnitVO> unitVoMap = unitVoS.stream().collect(Collectors.toMap(UnitVO::getCode, Function.identity()));

        //查询商品对应的单位列表
        List<GoodsUnitConversionVO> conversionList = iGoodsUnitConversionService.queryListByGoodsCodes(goods);
        Map<String, List<GoodsUnitConversionVO>> unitMap = new HashMap<>();
        for (int i = 0; i < goods.size(); i++) {
            String goodsCode = goods.get(i);
            List<GoodsUnitConversionVO> list = new ArrayList<>();
            for (int i1 = 0; i1 < conversionList.size(); i1++) {
                GoodsUnitConversionVO vonversion = conversionList.get(i1);
                if (goodsCode.equals(vonversion.getGoodsCode())) {
                    list.add(vonversion);
                }
            }
            if (list.size() > 0) {
                unitMap.put(goodsCode, list);
            }
            conversionList.removeAll(list);
        }

        List<GoodsVO> collect = query.stream().map(info -> {
            StringBuilder buffer = new StringBuilder();
            String name = "(" + info.getName() + ")";
            buffer.append(name);
            String barCode = "(" + info.getBarCode() + ")";
            buffer.append(barCode);
            String goodCode = "(" + info.getGoodCode() + ")";
            buffer.append(goodCode);
            info.setName(buffer.toString());
            //单位列表
            List<GoodsUnitConversionVO> unitConversionVOList = unitMap.get(info.getCode());
            if (null != unitConversionVOList && unitConversionVOList.size() > 0) {
                info.setUnits(unitConversionVOList);
            }
            // 基本单位
            UnitVO unitVO = unitVoMap.get(info.getUnitCode());
            if (null != unitVO) {
                info.setUnitName(unitVO.getUnitName());
            }
            return info;
        }).collect(Collectors.toList());
        return collect;
    }

    @Override
    public GoodsVO queryListSum(GoodsDTO goodsDTO) {
        return baseMapper.queryListSum(goodsDTO);
    }

    @Override
    public GoodsVO count(GoodsDTO goodsDTO) {

        return baseMapper.queryListSum(goodsDTO);
    }

    @Override
    public int batchDelete(List<String> list) {
        return baseMapper.batchDelete(list);
    }

     /**
      *  @author: Allen chen
      *  @Date: 2020/10/8 9:45
      *  @Description:1，库存条件：
      *                 1.1库存数量>0 不可以停用
      */
    private Map<Boolean, List<Map<String, Object>>> checkQuantity(List<String> codes){
        List<Map<String, Object>> maps = baseMapper.queryQuantity(codes);
        return maps.stream().collect(Collectors.groupingBy(s -> MapUtil.getInt(s, "quantity") == 0));
    }

    private List<String> checkReceiveItem(List<String> codes){
        List<ReceiveItemVO> receiveItems = iReceiveItemService.getReceiveItemByGoods(codes);
        List<String> goodCodes = receiveItems.stream().filter(info -> info.getReceivedQuantity().compareTo(BigDecimal.ZERO) == 0
                || info.getReceivedQuantity().compareTo(info.getInboundQuantity()) > 0).map(info -> {
            return info.getGoodsCode();
        }).collect(Collectors.toList());
        return goodCodes;
    }

    @Override
    public List<GoodsVO> queryList(GoodsDTO goodsDTO) {
        if (StrUtil.isNotEmpty(goodsDTO.getSortField())) {
            goodsDTO.setSortField(CommonUtil.camel2Underline(goodsDTO.getSortField()));

            if ("type_text".equals(goodsDTO.getSortField())){
                goodsDTO.setSortField("a.type");
            }
            if ("unit_name".equals(goodsDTO.getSortField())){
                goodsDTO.setSortField("a.unit_code");
            }
            if ("lot_rule".equals(goodsDTO.getSortField())){
                goodsDTO.setSortField("a.lot_number");
            }
            if ("storage_strategy_text".equals(goodsDTO.getSortField())){
                goodsDTO.setSortField("a.storage_strategy");
            }
        }
        List<String> typeCodes = new ArrayList<>();
        List<String> brandCodes = new ArrayList<>();
        List<String> userCodes = new ArrayList<>();
        List<String> unitCodes = new ArrayList<>();
        List<String> ruleCodes = new ArrayList<>();

        List<GoodsVO> goodsVoS = baseMapper.queryList(goodsDTO);
        if(null == goodsVoS || goodsVoS.size() <= 0){
            return new ArrayList<GoodsVO>();
        }
        goodsVoS.stream().forEach(info -> {
            typeCodes.add(info.getType());
            brandCodes.add(info.getBrandCode());
            unitCodes.add(info.getUnitCode());
            userCodes.add(info.getModifiedBy());
            userCodes.add(info.getCreatedBy());
            ruleCodes.add(info.getLotNumber());
        });
        // 查询商品分类
        List<GoodsType> goodTypes = iGoodsTypeService.getListByCodes(typeCodes.stream().distinct().collect(Collectors.toList()));
        goodTypes.stream().collect(Collectors.toMap(GoodsType::getCode, Function.identity()));
        // 查询品牌
        List<BrandVO> brands = iBrandService.getListByCodes(brandCodes.stream().distinct().collect(Collectors.toList()));
        Map<String, BrandVO> brandsMap = brands.stream().collect(Collectors.toMap(BrandVO::getCode, Function.identity()));
        //查询单位
        List<UnitVO> unitVoS = iGoodsUnitService.queryUnitsByCodes(unitCodes.stream().distinct().collect(Collectors.toList()));
        Map<String, UnitVO> unitVoMap = unitVoS.stream().collect(Collectors.toMap(UnitVO::getCode, Function.identity()));

        List<BatchRuleVO> listByCodes = iBatchRuleService.getListByCodes(ruleCodes.stream().distinct().collect(Collectors.toList()));
        Map<String, BatchRuleVO> ruleMap = listByCodes.stream().collect(Collectors.toMap(BatchRuleVO::getCode, Function.identity()));

        // TODO: 2020/9/1 查询用户信息

        List<GoodsVO> collect = goodsVoS.stream().map(info -> {
            StatusEnum enumByValue = StatusEnum.getEnumByValue(info.getStatus());
            if (null != enumByValue) {
                info.setStatusText(enumByValue.getDesc());
            }
            info.setTypeText(goodsTypeCache.translate(info.getTypeCode()));
            BrandVO brandVO = brandsMap.get(info.getBrandCode());
            if (null != brandVO) {
                info.setBrandText(brandVO.getName());
            }
            UnitVO unitVO = unitVoMap.get(info.getUnitCode());
            if (null != unitVO) {
                info.setUnitName(unitVO.getUnitName());
            }
            BatchRuleVO batchRule = ruleMap.get(info.getLotNumber());
            if(null != batchRule){
                info.setLotRule(batchRule.getName());
            }
            StorageStrategyEnum enumByCode = StorageStrategyEnum.getEnumByValue(info.getStorageStrategy());
            if (null != enumByCode) {
                info.setStorageStrategyText(enumByCode.getDesc());
            }
            return info;
        }).collect(Collectors.toList());
        return collect;
    }

     /**
      *  @author: Allen chen
      *  @Date: 2020/10/9 8:54
      *  @Description:1，库存条件：
      *                 1.1库存数量>0 不可以停用
      *             2，收货单条件：
      *                 2.1，收货数量=0时（收货单状态为新增或待收货），不可以停用
      *                 2.2，收货数量=入库数量时，可以认为收货单完成，可以停用
      *                 2.3，收货数量 > 入库数量 （入库没完成应该是作业中），不可以停用
      *             3，期初单条件：
      *                 3.1,期初表中新增状态的的数据 不可以停用
      *             4，盘点条件;
      *                 4.1，状态为作废和已审核的可以停用，其他状态不可以
      *             5, 差异单条件：
      *                 5.1，未审核的差异单不能停用----
      */
    @Override
    public Map<String, Object> batchUpdateStatus(List<String> codes, String status) {
        Map<String, Object> result = new HashMap<>();
       List<String> canOpt = new ArrayList<>();
       int num = 0;
        if(status.equals(StatusEnum.UNACTIVE.getValue())){
             canOpt.addAll(codes);
            //库存校验
            Map<Boolean, List<Map<String, Object>>> goodsMap = checkQuantity(codes);
            List<Map<String, Object>> use = goodsMap.get(false);
            if (IterUtil.isNotEmpty(use)) {
                //库存不为0 需要提示的商品名称
                List<String> useCode = use.stream().map(r -> MapUtil.getStr(r, "goodsCode")).collect(Collectors.toList());
                List<String> useName = useCode.stream().map(code -> goodsCache.translate(code)).collect(Collectors.toList());
                canOpt.removeAll(useCode);
                result.put("quantityUse",useName);
            }
            List<String> receiveUse = checkReceiveItem(codes);
            if(!receiveUse.isEmpty()){
                //收货单使用中的goodName
                List<String> useName = receiveUse.stream().map(r -> goodsCache.translate(r)).collect(Collectors.toList());
                canOpt.removeAll(receiveUse);
                result.put("receiveUse",useName);
           }
                //期初数据使用中
            List<String> initUse = iInitializationService.queryByGoodsCode(codes);
            if(!initUse.isEmpty()){
                List<String> useName = initUse.stream().map(r -> goodsCache.translate(r)).collect(Collectors.toList());
                canOpt.removeAll(initUse);
                result.put("initUse",useName);
            }
            //盘点数据使用中
            List<String> countingUse = iCountingService.queryByGoodsCode(codes);
            if(!countingUse.isEmpty()){
                List<String> useName = countingUse.stream().map(info -> goodsCache.translate(info)).collect(Collectors.toList());
                canOpt.removeAll(countingUse);
                result.put("countingUse",useName);
            }
            //差异单使用的数据
            List<String> diffUse = iDifferenceService.queryByGoodscode(codes);
            if(!diffUse.isEmpty()){
                List<String> useName = diffUse.stream().map(info -> goodsCache.translate(info)).collect(Collectors.toList());
                canOpt.removeAll(diffUse);
                result.put("diffUse",useName);
            }
            if(!canOpt.isEmpty()){
                num = updateStatus(canOpt, status);
            }
        }else {
            num = updateStatus(codes, status);
        }
        result.put("success", num);
        return result;
    }

    private int updateStatus(List<String> codes,String status){
        int num = 0;
        boolean flag = false;
        if (IterUtil.isNotEmpty(codes)) {
            for (String code : codes) {
                if (status.equals(StatusEnum.ACTIVE.getValue())) {
                    flag = this.update(new LambdaUpdateWrapper<Goods>()
                            .set(Goods::getStatus, status)
                            .set(Goods::getDeleteFlag, DeleteFlagEnum.NOTDELETE.getCode().toString())
                            .eq(Goods::getPartyGroupCode, UserUtil.getBranchCode())
                            .eq(Goods::getCode, code));
                } else {
                    flag = this.update(new LambdaUpdateWrapper<Goods>().set(Goods::getStatus, status)
                            .eq(Goods::getPartyGroupCode, UserUtil.getBranchCode())
                            .eq(Goods::getCode, code));
                }
                if (flag) {
                    goodsCache.flushCache(code);
                    num++;
                }
            }
        }
        return num;
    }


    /**
     * 通过code删除
     *
     * @param codes
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String,Object> removeByCodes(List<String> codes) {
        Map<String,Object> result = new HashMap<>();
        int num = 0;
        List<GoodsVO> goodsVoS = queryByCodes(codes);
        List<String> notDelete = goodsVoS.stream().filter(info -> info.getDeleteFlag().intValue() == DeleteFlagEnum.NOTDELETE.getCode().intValue()).map(r -> {
            return r.getCode();
        }).collect(Collectors.toList());
        List<String> notDeleteNames = notDelete.stream().map(code -> {
            return goodsCache.translate(code);
        }).collect(Collectors.toList());
        result.put("notDelete",notDeleteNames);
        List<String> canDelete = codes.stream().filter(code -> !notDelete.contains(code)).collect(Collectors.toList());
        if(IterUtil.isNotEmpty(canDelete)){
            num = delete(canDelete);
        }
        result.put("success",num);
        return result;
    }
    private int delete(List<String> codes){
        int num = 0;
        for (String code:codes) {
            int delete = baseMapper.delete(new UpdateWrapper<Goods>().lambda().eq(Goods::getCode, code).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
            if(delete > 0){
                iGoodsUnitConversionService.remove(new QueryWrapper<GoodsUnitConversion>().lambda().eq(GoodsUnitConversion::getGoodsCode, code).eq(GoodsUnitConversion::getGroupCode,UserUtil.getBranchCode()));
                goodsCache.flushCache(code);
                goodsPnService.remove(new LambdaQueryWrapper<GoodsPn>().eq(GoodsPn::getGoodsCode, code));
            }
            num++;
        }
        return num;
    }

    /**
     * 模糊查询商品
     *
     * @param keywords     商品名称、编码、条码
     * @param shipmentCode
     */
    @Override
    public List<Map<String, Object>> search(String keywords, String shipmentCode) {
        List<String> goodsCodes = baseMapper.searchGoodsCode(keywords,shipmentCode,UserUtil.getBranchCode());
        return goodsCodes.parallelStream().map(s->goodsCache.get(s)).collect(Collectors.toList());
    }

    /**
     * 同步时，根据操作类型判断是插入还是更新
     *
     * @param request
     * @param shipmentName
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void batchGoodsSaveOrUpdate(ItemsSynchronizeRequest request, String shipmentName, Map<String,String> unitMap){
        String actionType = request.getActionType();
        String shipmentPartyCode = request.getOwnerCode();
        List<ItemsSynchronizeRequest.Item> items = request.getItems();
        List<Goods> goodsList = Lists.newArrayList();
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        for (ItemsSynchronizeRequest.Item item : items) {
            Goods goods = new Goods();
            String unitName = item.getStockUnit();
            goods.setShipmentCode(shipmentPartyCode);
            goods.setShipmentName(shipmentName);
            goods.setGoodCode(item.getItemCode());
            goods.setName(item.getItemName());
            goods.setBarCode(item.getBarCode());
            goods.setTypeCode(item.getCategoryId());
            goods.setBrandCode(item.getBrandCode());
            goods.setUnitCode(unitMap.get(shipmentPartyCode+"_"+unitName));
            goods.setTypeName(item.getCategoryName());
            goods.setLotNumber(item.getBatchCode());
            if (ObjectUtil.isNotNull( item.getShelfLife())) {
                goods.setQuality(new BigDecimal(item.getShelfLife()));
            }
            if (ObjectUtil.isNotNull(item.getAdventLifecycle())) {
                goods.setEarlyWarning(new BigDecimal(item.getAdventLifecycle()));
            }
            if (StrUtil.isNotEmpty(item.getGrossWeight())) {
                goods.setWeight(new BigDecimal(item.getGrossWeight()));
            }
            if (StrUtil.isNotEmpty(item.getCreateTime())) {
                goods.setGmtCreated(LocalDateTime.parse(item.getCreateTime(),df));
            }
            if (StrUtil.isNotEmpty(item.getUpdateTime())) {
                goods.setGmtModified(LocalDateTime.parse(item.getUpdateTime(),df));
            }
            goods.setRemark(item.getRemark());
            goods.setItemType(item.getItemType());
            goodsList.add(goods);
        }
        if (CommonConst.ADD.equals(actionType)) {
            // 拼接sql，批量执行新增操作
            sysnBatchSave(goodsList);
        }

        if (CommonConst.UPDATE.equals(actionType)) {
            // 拼接sql，批量执行更新操作
            sysnBatchUpdate(goodsList);
        }
    }

    /**
     * 单个商品同步时，根据操作类型判断是插入还是更新
     *
     * @param request
     * @param shipmentName
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void singleGoodsSaveOrUpdate(SingleitemSynchronizeRequest request, String shipmentName, Map<String,String> unitMap){
        log.info("《《《《《 同步商品开始 2》》》》》");
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        Goods goods = new Goods();
        String unitName = request.getItem().getStockUnit();
        String shipmentPartyCode = request.getOwnerCode();
        String actionType = request.getActionType();
        goods.setShipmentCode(shipmentPartyCode);
        goods.setShipmentName(shipmentName);
        goods.setGoodCode(request.getItem().getItemCode());
        goods.setName(request.getItem().getItemName());
        goods.setBarCode(request.getItem().getBarCode());
        goods.setTypeCode(request.getItem().getCategoryId());
        goods.setBrandCode(request.getItem().getBrandCode());
        goods.setUnitCode(unitMap.get(shipmentPartyCode+"_"+unitName));
        goods.setTypeName(request.getItem().getCategoryName());
        goods.setLotNumber(request.getItem().getBatchCode());
        if (ObjectUtil.isNotNull( request.getItem().getShelfLife())) {
            goods.setQuality(new BigDecimal(request.getItem().getShelfLife()));
        }
        if (ObjectUtil.isNotNull(request.getItem().getAdventLifecycle())) {
            goods.setEarlyWarning(new BigDecimal(request.getItem().getAdventLifecycle()));
        }
        if (StrUtil.isNotEmpty(request.getItem().getGrossWeight())) {
            goods.setWeight(new BigDecimal(request.getItem().getGrossWeight()));
        }
        if (StrUtil.isNotEmpty(request.getItem().getCreateTime())) {
            goods.setGmtCreated(LocalDateTime.parse(request.getItem().getCreateTime(),df));
        }
        if (StrUtil.isNotEmpty(request.getItem().getUpdateTime())) {
            goods.setGmtModified(LocalDateTime.parse(request.getItem().getUpdateTime(),df));
        }
        goods.setRemark(request.getItem().getRemark());
        goods.setItemType(request.getItem().getItemType());
        if (CommonConst.ADD.equals(actionType)) {
            goods.setCode(IdUtil.simpleUUID());
            //单位编码和条码 同一个货主下不能重复
            checkGoodsCodeAndBarCode(goods);
            super.save(goods);
            goodsCache.flushAllCacheByGroupCode(UserUtil.getBranchCode());
            goodsCache.flushAllCacheByShipmentCode(goods.getShipmentCode());
        }
        if (CommonConst.UPDATE.equals(actionType)) {
            this.update(goods, new QueryWrapper<Goods>().lambda().eq(Goods::getShipmentCode, shipmentPartyCode).eq(Goods::getGoodCode, request.getItem().getItemCode()).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
        }
        log.info("《《《《《 同步商品结束 2》》》》》");

    }

    /**
     * 同步-批量保存操作
     * @param goodsList
     * @return
     */
    private int sysnBatchSave(List<Goods> goodsList) {
        StringBuilder data = new StringBuilder();
        data.append("INSERT INTO `base_goods`(`id`, `code`,`group_code`, `shipment_code`, `shipment_name`,   "
                + "`type_code`, `brand_code`,  `good_code`, `bar_code`, `unit_code`, `type_name`, "
                + "`type`,  `name`, `storage_strategy`, `quality`, `early_warning`,  `weight`,`volume`,  "
                + " `status`,`gmt_created`, `gmt_modified`, `remark`) values ");
        String sql = goodsList.stream().map(e -> makeInsertValueStr(e)).collect(Collectors.joining(","));
        data.append(sql);
        return this.baseMapper.execSql(data.toString());
    }

    /**
     * 同步-批量插入生成sql
     * @param goods
     * @return
     */
    private StringBuilder makeInsertValueStr(final Goods goods) {
        LocalDateTime nowTime = LocalDateTime.now();
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append(DEFAULT);
        // code
        data.append("'").append(IdUtil.simpleUUID()).append("', ");
        // group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // shipment_code
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getShipmentCode(), StrUtil.EMPTY)).append("', ");
        // shipment_name
        data.append("'").append(goods.getShipmentName()).append("', ");
        // type_code
        data.append("'").append(goods.getTypeCode()).append("', ");
        // brand_code
        data.append("'").append( StrUtil.EMPTY).append("', ");
        // good_code
        data.append("'").append(goods.getGoodCode()).append("', ");
        // bar_code
        data.append("'").append(goods.getBarCode()).append("', ");
        // unit_code
        data.append("'").append(goods.getUnitCode()).append("', ");
        // type_name
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getTypeName(), StrUtil.EMPTY)).append("', ");
        // type
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getType(), StrUtil.EMPTY)).append("', ");
        // name
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getName(), StrUtil.EMPTY)).append("', ");
        // storage_strategy
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getStorageStrategy(), StrUtil.EMPTY)).append("', ");
        // quality
        data.append("'").append(goods.getQuality()).append("', ");
        // early_warning
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getEarlyWarning(), BigDecimal.ZERO)).append("', ");
        // weight
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getWeight(), BigDecimal.ZERO)).append("', ");
        // volume
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getVolume(), BigDecimal.ZERO)).append("', ");
        // status
        data.append("'").append(NumConst.NUM_ZERO).append("', ");
        // gmt_created
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getGmtCreated(), nowTime)).append("', ");
        // gmt_modified
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getGmtModified(), nowTime)).append("', ");
        // remark
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getRemark(),StrUtil.EMPTY)).append("' ");
        data.append(")");
        return data;
    }

    /**
     * 同步商品-批量更新
     * @param goodsList
     * @return
     */
    private int sysnBatchUpdate(List<Goods> goodsList) {
        StringBuilder data = new StringBuilder();
        data.append("REPLACE INTO `base_goods`(`id`, `code`, `shipment_code`, `shipment_name`,   "
                + "`type_code`, `brand_code`,  `good_code`, `bar_code`, `unit_code`, `type_name`, "
                + "`type`,  `name`, `storage_strategy`, `quality`, `early_warning`,  `weight`,`volume`,  "
                + " `status`,`gmt_created`, `gmt_modified`, `remark`) values ");
        String sql = goodsList.stream().map(e -> makeUpdateValueStr(e)).collect(Collectors.joining(","));
        data.append(sql);
        return this.baseMapper.execSql(data.toString());
    }

    /**
     * 同步商品-批量更新生成sql
     * @param goods
     * @return
     */
    private StringBuilder makeUpdateValueStr(final Goods goods) {
        LocalDateTime nowTime = LocalDateTime.now();
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append(goods.getId()).append(",");
        // code
        data.append("'").append(goods.getCode()).append("', ");
        // shipment_code
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getShipmentCode(), StrUtil.EMPTY)).append("', ");
        // shipment_name
        data.append("'").append(goods.getShipmentName()).append("', ");
        // type_code
        data.append("'").append(goods.getTypeCode()).append("', ");
        // brand_code
        data.append("'").append( StrUtil.EMPTY).append("', ");
        // good_code
        data.append("'").append(goods.getGoodCode()).append("', ");
        // bar_code
        data.append("'").append(goods.getBarCode()).append("', ");
        // unit_code
        data.append("'").append(goods.getUnitCode()).append("', ");
        // type_name
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getTypeName(), StrUtil.EMPTY)).append("', ");
        // type
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getType(), StrUtil.EMPTY)).append("', ");
        // name
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getName(), StrUtil.EMPTY)).append("', ");
        // storage_strategy
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getStorageStrategy(), StrUtil.EMPTY)).append("', ");
        // quality
        data.append("'").append(goods.getQuality()).append("', ");
        // early_warning
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getEarlyWarning(), BigDecimal.ZERO)).append("', ");
        // weight
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getWeight(), BigDecimal.ZERO)).append("', ");
        // volume
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getVolume(), BigDecimal.ZERO)).append("', ");
        // status
        data.append("'").append(NumConst.NUM_ZERO).append("', ");
        // gmt_created
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getGmtCreated(), nowTime)).append("', ");
        // gmt_modified
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getGmtModified(), nowTime)).append("', ");
        // created_name
        // remark
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getRemark(),StrUtil.EMPTY)).append("' ");
        data.append(")");
        return data;
    }


    /**
     * 校验数据必填项
     * @param dto
     */
    public void validateNotBlank1(GoodsExcelDTO dto, Integer index){
        // 校验Excel里必填项是否有空值
        if (StrUtil.isBlank(dto.getShipmentSerialNumber())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getTypeName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSTYPE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getGoodCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSCODE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getGoodsBarCode())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_BAR_CODE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getGoodsName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_NAME + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getUnitName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BASICUNIT_NAME + NOT_BLANK);
        }
        if (!isNumeric(dto.getUnitPrecision())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + UNITPRECISION + FORMAT_ERROR);
        }
        if (StrUtil.isBlank(dto.getLotNumberName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + LOTNUMBERNAME + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getTypeName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSTYPE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getSaleName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SALE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getPurchaseName())){
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + PURCHASE + NOT_BLANK);
        }
        if (StrUtil.isBlank(dto.getStorageStrategyName())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + STORAGESTRATEGY + NOT_BLANK);
        }
    }

    /**
     * 校验数据必填项2， 由于代码校验要求不能超过15个if判断，所以拆分为多个
     * @param dto
     */
    public void validateNotBlank2(GoodsExcelDTO dto, Integer index){
        // 校验Excel里必填项是否有空值
        if (StrUtil.isBlank(dto.getQuality())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUALITY + NOT_BLANK);
        }
        // 校验是否是空或者数字
        if (!isNumeric(dto.getQuality())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUALITY + FORMAT_ERROR);
        }

        if (!isNumeric(dto.getEarlyWarning())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + EARLYWARNING + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getReceivingWarning())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVINGWARNING + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getSendWarning())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SENDWARNING + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getQuality())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUALITY + FORMAT_ERROR);
        }

        if (!isNumeric(dto.getLength())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + LENGTH + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getWidth())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WIDTH + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getHeight())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + HEIGHT + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getWeight())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WEIGHT + FORMAT_ERROR);
        }
        if (!isNumeric(dto.getVolume())) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + VOLUME + FORMAT_ERROR);
        }

        // 校验备注字段长度，不能超过255
        String remarks = dto.getRemarks();
        if (StrUtil.isNotBlank(remarks) && StrUtil.length(remarks) > TWO_HUNDRED_FIFTY_FIVE.intValue()) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + REMARKS_NAME + TOO_LONG);
        }

    }


    /**
     * 校验商品信息是否有重复项
     * @param repeatData
     * @param dto
     * @param index
     */
    public void validateRepeatData(Map<String, Integer> repeatData, GoodsExcelDTO dto, Integer index){
        String key = dto.getShipmentSerialNumber() + dto.getGoodCode();
        Integer oldIndex = repeatData.get(key);
        if (oldIndex != null) {
            throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + AND_NAME + TIP_PREFIX + (oldIndex + NUM_THREE) + LINE + REPEAT_DATA);
        }
        // 没有重复则将新数据加入到校验组中
        repeatData.put(key, index);
    }


    static Pattern pattern = Pattern.compile("^[0-9]*.?\\d*$");
    public static boolean isNumeric(String str) {
        if (StrUtil.isBlank(str)) {
            return true;
        }
        str = str.trim();
        return pattern.matcher(str).matches();
    }

    /**
     * 商品Excel预览
     * @param list
     * @return
     */
    @Override
    public List<GoodsExcelVO> goodsExcelView(List<GoodsExcelDTO> list) {
        // 导入号
        String importCode = IdUtil.simpleUUID();
        List<Shipment> shipmentList = shipmentService.list();

        // 校验重复项
        Map<String, Integer> repeatData = new HashMap<>();

        // 货主数据相对较少且没有太多关联关系，直接生成map减少遍历
        final Map<String, Shipment> shipmentMap = shipmentList.stream().collect(Collectors.toMap(e->e.getPartyCode(), e->e));
        // 商品类型等， 使用map用于减少对缓存的依赖
        Map<String, GoodsType> goodsTypeMap = new HashMap<>();
        Map<String, GoodsUnit> goodsUnitMap = new HashMap<>();
        Map<String, BatchRule> batchRuleMap = new HashMap<>();
        Map<String, Brand> brandMap = new HashMap<>();

        // 遍历序号
        AtomicInteger indexes = new AtomicInteger(0);
        List<GoodsExcelVO> finalList = list.stream().map(temp-> {
            Integer index = indexes.getAndIncrement();

            //必填校验， 由于代码校验要求单个方法if校验不能超过15个，所以拆分为两次校验
            validateNotBlank1(temp, index);
            validateNotBlank2(temp, index);
            //重复数据校验
            validateRepeatData(repeatData, temp, index);

            //判断货主是否存在
            GoodsExcelVO vo = new GoodsExcelVO();
            BeanUtils.copyProperties(temp, vo);

            // 校验、赋值货主信息
            Shipment shipment = shipmentMap.get(vo.getShipmentSerialNumber());
            if (shipment == null) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_EXIST);
            }
            if (!UserUtil.getAuthorizeShipmentCodes().contains(shipment.getPartyCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + YU + LOGIN_SHIPMENT + NOT_SAMEB);
            }
            vo.setShipmentName(shipment.getUserName());
            vo.setShipmentCode(shipment.getPartyCode());

            // 校验商品类型
            GoodsType type = goodsTypeMap.get(vo.getShipmentCode() + "-" + vo.getGoodsTypeName());
            if (type == null) {
                type = goodsTypeCache.getByName(vo.getGoodsTypeName(), vo.getShipmentCode(), UserUtil.getBranchCode());
                if (type == null) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODTYPE + NOT_EXIST);
                } else {
                    goodsTypeMap.put(vo.getShipmentCode() + "-" + vo.getGoodsTypeName(), type);
                }
            }
            vo.setGoodsTypeCode(type.getCode());

            // 校验商品单位
            GoodsUnit unit = goodsUnitMap.get(vo.getShipmentCode() + "-" + vo.getUnitName());
            if (unit == null) {
                unit = unitCache.getByUnitName(vo.getUnitName(), vo.getShipmentCode(),UserUtil.getBranchCode());
                if (unit == null) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BASICUNIT_NAME + NOT_EXIST);
                } else {
                    goodsUnitMap.put(vo.getShipmentCode() + "-" + vo.getUnitName(), unit);
                }
            }
            if (!StatusEnum.ACTIVE.getValue().equals(unit.getStatus())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_UNIT + NOT_ENABLED);
            }
            vo.setUnitCode(unit.getCode());
            vo.setUnitName(unit.getUnitName());
            vo.setUnitNum(unit.getUnitCode());

            // 校验批次规则
            BatchRule batchRule = batchRuleMap.get(vo.getShipmentCode() + "-" + vo.getLotNumberName());
            if (batchRule == null) {
                batchRule = batchRuleCache.getByName(vo.getLotNumberName(), vo.getShipmentCode(),UserUtil.getBranchCode());
                if(null == batchRule){
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + LOTNUMBERNAME + NOT_EXIST);
                } else {
                    batchRuleMap.put(vo.getShipmentCode() + "-" + vo.getLotNumberName(), batchRule);
                }
            }

            vo.setLotNumber(batchRule.getCode());

            // 校验商品分类， 只有 成品、半成品、原料 3种
            GoodsTypeEnum enumByDesc = GoodsTypeEnum.getEnumByDesc(vo.getTypeName());
            if(null == enumByDesc){
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSTYPE + NOT_EXIST);
            }
            vo.setType(enumByDesc.getCode());

            // 校验“是否可销售”字段
            if (!StrUtil.equalsAny(vo.getSaleName(), "是", "否")) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + IS_SALE_NAME + MUST_BE);
            }
            vo.setSale(vo.getSaleName().indexOf("否") > 0 ? "0" : "1");

            // 校验“是否可采购”字段
            if (!StrUtil.equalsAny(vo.getPurchaseName(), "是", "否")) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + IS_PURCHASE_NAME + MUST_BE);
            }
            vo.setPurchase(vo.getPurchaseName().indexOf("否") > 0 ? "0" : "1");

            // 校验“混放策略”字段
            StorageStrategyEnum storageStrategy = StorageStrategyEnum.getEnumByDesc(vo.getStorageStrategyName());
            if(null == storageStrategy){
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + STORAGESTRATEGY + NOT_EXIST);
            }
            vo.setStorageStrategy(storageStrategy.getValue());

            // 校验品牌
            if(StringUtils.isNotEmpty(vo.getBrandName())){
                Brand br = brandMap.get(vo.getShipmentCode() + "-" + vo.getBrandName());
                if (br == null) {
                    br = brandCache.getByName(vo.getBrandName(), vo.getShipmentCode(),UserUtil.getBranchCode());
                }
                if (br == null) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + BRANDNAME + NOT_EXIST);
                }
                brandMap.put(vo.getShipmentCode() + "-" + vo.getBrandName(), br);
                vo.setBrandCode(br.getCode());
            }

            // 件装量默认为1
            vo.setQuantity(NUM_ONE.toString());
            vo.setImportCode(importCode);

            return vo;
        }).collect(Collectors.toList());
        // 保存到缓存中
        importTempCache.putTempData(importCode, finalList);
        return finalList;
    }

    /**
     * 导入商品信息
     * @param importCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result importGoods(String importCode) {
        // 根据导入码，获取导入缓存
        List<GoodsExcelVO> tempData = importTempCache.getTempData(importCode, GoodsExcelVO.class);
        // 判断数据，如果没有了，则直接返回
        if(tempData == null){
            throw new ServiceException("缓存数据不存在，或超过有效时间，缓存已被清除，请重新导入。");
        }

        // 批量导入
        batchImportSaveOrUpdate(tempData);

        // 完成导入后删除导入的缓存
        importTempCache.removeCache(importCode);

        return Result.ok();
    }

    /**
     * 根据货主、商品编码获取商品信息
     * @param shipmentCode
     * @param goodCode
     * @return
     */
    @Override
    public Goods getByShipmentAndGoodCode(String shipmentCode, String goodCode) {
        List<Goods> goodsList = goodsCache.getAllObjByShipmentCode(shipmentCode);
        if (IterUtil.isEmpty(goodsList)) {
            return null;
        }
        goodsList = goodsList.stream().filter(e->StrUtil.equals(goodCode, e.getGoodCode())).collect(Collectors.toList());
        if (!IterUtil.isEmpty(goodsList)) {
            return goodsList.get(NUM_ZERO);
        }

        return null;
    }

    /**
     * 生成并执行批量导入sql
     * @param tempData
     */
    @Transactional(rollbackFor = {Exception.class})
    public Map<String, Boolean> batchImportSaveOrUpdate(List<GoodsExcelVO> tempData) {
        // 货主信息，用于更新缓存时使用
        Map<String, Boolean> shipmentCodes = new HashMap<>();

        Map<String, Boolean> updateGoodsCodes = new HashMap<>();
        // 插入数据列表、更新数据列表
        List<GoodsExcelVO> insertGoodsList = Lists.newArrayList();
        List<GoodsExcelVO> updateGoodsList = Lists.newArrayList();

        // 获取涉及到的货主下的商品缓存Map，key值为“货主code-商品serialNumber”，用于快速判断数据是插入还是修改
        Map<String, Goods> goodsMap = getImportShipmentGoodsMap(tempData);

        tempData.stream().forEach(e->{
            Goods sameOne = goodsMap.get(e.getShipmentCode() + "-" + e.getGoodCode());
            if (sameOne == null) {
                // 没有相同数据，添加到插入列表中
                insertGoodsList.add(e);
            } else {
                copyUpdateProperties(sameOne, e);
                updateGoodsList.add(e);

                updateGoodsCodes.put(sameOne.getCode(), true);
            }
            shipmentCodes.put(e.getShipmentCode(), true);
        });

        if (!shipmentCodes.isEmpty()) {
            shipmentCodes.forEach((k,v)->{
                goodsCache.flushAllCacheByShipmentCode(k);
            });
        }

        // 删除旧的基本单位
        if (!updateGoodsCodes.isEmpty()) {
            iGoodsUnitConversionService.remove(new QueryWrapper<GoodsUnitConversion>().lambda().eq(GoodsUnitConversion::getFlag, NUM_ONE).eq(GoodsUnitConversion::getGroupCode,UserUtil.getBranchCode())
                    .in(GoodsUnitConversion::getGoodsCode, updateGoodsCodes.keySet()));
            updateGoodsCodes.forEach((k,v)->{
                goodsUnitConversionCache.flushCache(k);
            });
        }

        // 拼接sql，批量执行新增、更新操作
        if (IterUtil.isNotEmpty(insertGoodsList)) {
            importBatchSave(insertGoodsList);
        }

        if (IterUtil.isNotEmpty(updateGoodsList)) {
            importBatchUpdate(updateGoodsList);
        }

        // 清理涉及到的货主的商品缓存，并更新这些货主下的商品缓存
        refreshCache(shipmentCodes,UserUtil.getBranchCode());

        return shipmentCodes;
    }

    /**
     * 删除老缓存，重建新缓存
     * @param shipmentCodes
     * @param groupCode
     */
    @Async
    public void refreshCache(Map<String, Boolean> shipmentCodes,String groupCode) {
        shipmentCodes.forEach((shipmentCode, flag)->{
            goodsCache.flushAllCacheByShipmentCode(shipmentCode);
            goodsCache.refreshByShipmentCode(shipmentCode,groupCode);
        });
    }

    /**
     * 批量插入商品、商品单位信息
     * @param insertGoodsList
     */
    @Transactional(rollbackFor = {Exception.class})
    public int importBatchSave(List<GoodsExcelVO> insertGoodsList) {
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        LocalDateTime nowTime = LocalDateTime.now();
        StringBuilder goodsSql = new StringBuilder();
        StringBuilder unitConversionSql = new StringBuilder();
        // 商品sql
        goodsSql.append("insert into `base_goods` ( `id`, `code`,`party_group_code`, `shipment_code`, `shipment_name`, `type_code`, `type_name`,"
                + " `good_code`, `bar_code`, `name`, `unit_code`, `unit_precision`, `spec`, `lot_number`, `type`, `sale`, "
                + "`purchase`, `quality`, `storage_strategy`, `early_warning`, `receiving_warning`, `send_warning`, `brand_code`,"
                + " `remark`, `status`, `is_deleted`, `created_by`, `gmt_created`, `modified_by`, `gmt_modified`, `created_name`, `modified_name`,`record_type`) values ");
        // 商品单位转换表sql
        unitConversionSql.append("INSERT INTO `base_goods_unit_conversion` (`id`, `code`, `group_code`,`goods_code`, `unit_code`, "
                + " `unit_name`, `quantity`, `length`, `width`, `height`, `weight`, `volume`, `flag`,`is_deleted`, "
                + " `created_by`, `gmt_created`, `modified_by`, `gmt_modified`, `created_name`, `modified_name`) values ");

        List<String> goodsDataList = new ArrayList<>();
        List<String> unitConversionDataList = new ArrayList<>();
        Set<String> goodsCodes = new HashSet<>();

        for (GoodsExcelVO e : insertGoodsList) {
            String goodsCode = IdUtil.simpleUUID();
            goodsDataList.add(makeInsertValuesStr(e, goodsCode, createdBy, createdName, nowTime, StatusEnum.UNACTIVE.getValue()));

            // 生成商品单位转换列表信息
            GoodsUnitConversion unitConversion = new GoodsUnitConversion();
            BeanUtil.copyProperties(e, unitConversion);
            unitConversion.setGoodsCode(goodsCode);
            goodsCodes.add(goodsCode);
            unitConversionDataList.add(makeUnitConversionValuesStr(unitConversion, createdBy, createdName, nowTime));
        }
        // 插入商品信息
        String goodsDataSql = goodsDataList.stream().collect(Collectors.joining(","));
        goodsSql.append(goodsDataSql);
        int goodsSize = this.baseMapper.execSql(goodsSql.toString());

        // 插入商品单位转换信息
        String unitConversionDataSql = unitConversionDataList.stream().collect(Collectors.joining(","));
        unitConversionSql.append(unitConversionDataSql);
        this.baseMapper.execSql(unitConversionSql.toString());

        return goodsSize;
    }

    /**
     * 生成单位转换表插入数据
     * @param conversion
     * @param createdBy
     * @param createdName
     * @param nowTime
     * @return
     */
    private String makeUnitConversionValuesStr(GoodsUnitConversion conversion, String createdBy, String createdName, LocalDateTime nowTime) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append(DEFAULT);
        // code
        data.append("'").append(IdUtil.simpleUUID()).append("',");
        // group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // goods_code
        data.append("'").append(conversion.getGoodsCode()).append("',");
        // unit_code
        data.append("'").append(conversion.getUnitCode()).append("',");
        // unit_name
        data.append("'").append(conversion.getUnitName()).append("',");
        // quantity
        data.append("'").append(NUM_ONE).append("',");
        // length
        data.append("'").append(ObjectUtil.defaultIfNull(conversion.getLength(), NUM_ZERO)).append("',");
        // width
        data.append("'").append(ObjectUtil.defaultIfNull(conversion.getWidth(), NUM_ZERO)).append("',");
        // height
        data.append("'").append(ObjectUtil.defaultIfNull(conversion.getHeight(), NUM_ZERO)).append("',");
        // weight
        data.append("'").append(ObjectUtil.defaultIfNull(conversion.getWeight(), NUM_ZERO)).append("',");
        // volume
        data.append("'").append(ObjectUtil.defaultIfNull(conversion.getVolume(), NUM_ZERO)).append("',");
        // flag
        data.append("'").append(NUM_ONE).append("',");
        // is_deleted
        data.append("'").append(NUM_ZERO).append("',");
        // created_by
        data.append("'").append(createdBy).append("',");
        // gmt_created
        data.append("'").append(nowTime).append("',");
        // modified_by
        data.append("'").append(createdBy).append("',");
        // gmt_modified
        data.append("'").append(nowTime).append("',");
        // created_name
        data.append("'").append(createdName).append("',");
        // modified_name
        data.append("'").append(createdName).append("'");
        data.append(")");

        return  data.toString();
    }


    /**
     * 导入-批量生成插入商品sql
     * @param excelVO
     * @param createdBy
     * @param createdName
     * @param nowTime
     * @return
     */
    private String makeInsertValuesStr(GoodsExcelVO excelVO, final String goodsCode, final String createdBy, final String createdName, final LocalDateTime nowTime, final String status) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append(DEFAULT);
        // code
        data.append("'").append(goodsCode).append("',");
        // party_group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // shipment_code
        data.append("'").append(excelVO.getShipmentCode()).append("',");
        // shipment_name
        data.append("'").append(excelVO.getShipmentName()).append("',");
        // type_code
        data.append("'").append(excelVO.getGoodsTypeCode()).append("',");
        // type_name
        data.append("'").append(excelVO.getGoodsTypeName()).append("',");
        // good_code
        data.append("'").append(excelVO.getGoodCode()).append("',");
        // bar_code
        data.append("'").append(excelVO.getGoodsBarCode()).append("',");
        // name
        data.append("'").append(excelVO.getGoodsName()).append("',");
        // unit_code
        data.append("'").append(excelVO.getUnitCode()).append("',");
        // unit_precision
        data.append("'").append(excelVO.getUnitPrecision()).append("',");
        // spec
        data.append("'").append(ObjectUtil.defaultIfNull(excelVO.getSpec(), StrUtil.EMPTY)).append("',");
        // lot_number
        data.append("'").append(excelVO.getLotNumber()).append("',");
        // type
        data.append("'").append(excelVO.getType()).append("',");
        // sale
        data.append("'").append(excelVO.getSale()).append("',");
        // purchase
        data.append("'").append(excelVO.getPurchase()).append("',");
        // quality
        data.append("'").append(excelVO.getQuality()).append("',");
        // storage_strategy
        data.append("'").append(excelVO.getStorageStrategy()).append("',");
        // early_warning
        data.append(excelVO.getEarlyWarning()).append(",");
        // receiving_warning
        data.append(excelVO.getReceivingWarning()).append(",");
        // send_warning
        data.append(excelVO.getSendWarning()).append(",");
        // brand_code
        data.append("'").append(ObjectUtil.defaultIfNull(excelVO.getBrandCode(), StrUtil.EMPTY)).append("',");
        // remark
        data.append("'").append(excelVO.getRemarks()).append("',");
        // status
        data.append("'").append(status).append("',");
        // is_deleted
        data.append("'").append(NUM_ZERO).append("',");
        // created_by
        data.append("'").append(createdBy).append("',");
        // gmt_created
        data.append("'").append(nowTime).append("',");
        // modified_by
        data.append("'").append(createdBy).append("',");
        // gmt_modified
        data.append("'").append(nowTime).append("',");
        // created_name
        data.append("'").append(createdName).append("',");
        // modified_name
        data.append("'").append(createdName).append("',");
        data.append(" 0 ");

        data.append(")");

        return  data.toString();
    }


    /**
     * 导入-商品批量更新操作
     * @param updateGoodsList
     */
    @Transactional(rollbackFor = {Exception.class})
    public int importBatchUpdate(List<GoodsExcelVO> updateGoodsList) {
        String createdBy = UserUtil.getCode();
        String createdName = partyCache.translate(createdBy);
        LocalDateTime nowTime = LocalDateTime.now();
        StringBuilder goodsSql = new StringBuilder();
        StringBuilder unitConversionSql = new StringBuilder();

        goodsSql.append("REPLACE INTO `base_goods`(`id`, `code`,`party_group_code`, `shipment_code`, `shipment_name`, `type_code`, `type_name`,"
                + " `good_code`, `bar_code`, `name`, `unit_code`, `unit_precision`, `spec`, `lot_number`, `type`, `sale`, "
                + "`purchase`, `quality`, `storage_strategy`, `early_warning`, `receiving_warning`, `send_warning`, `brand_code`,"
                + " `remark`, `status`, `is_deleted`, `created_by`, `gmt_created`, `modified_by`, `gmt_modified`, `created_name`, `modified_name`) values ");
        // 商品单位转换表sql
        unitConversionSql.append("INSERT INTO `base_goods_unit_conversion` (`id`, `code`,`group_code`, `goods_code`, `unit_code`, "
                + " `unit_name`, `quantity`, `length`, `width`, `height`, `weight`, `volume`, `flag`,`is_deleted`, "
                + " `created_by`, `gmt_created`, `modified_by`, `gmt_modified`, `created_name`, `modified_name`) values ");

        String goodsDataSql = updateGoodsList.stream().map(e->makeUpdateValuesStr(e, createdBy, createdName, nowTime, e.getStatus())).collect(Collectors.joining(","));
        goodsSql.append(goodsDataSql);

        int goodsSize = this.baseMapper.execSql(goodsSql.toString());

        // 生成单位转换表数据的sql
        String unitConversionDataSql = updateGoodsList.stream().map(e->{
            GoodsUnitConversion conversion = new GoodsUnitConversion();
            BeanUtil.copyProperties(e, conversion, CopyOptions.create().setIgnoreNullValue(true));
            conversion.setGoodsCode(e.getCode());
            return makeUnitConversionValuesStr(conversion, createdBy, createdName, nowTime);
        }).collect(Collectors.joining(","));
        unitConversionSql.append(unitConversionDataSql);

        this.baseMapper.execSql(unitConversionSql.toString());

        return goodsSize;
    }


    /**
     * 导入-批量生成插入商品sql
     * @param goods
     * @param createdBy
     * @param createdName
     * @param nowTime
     * @return
     */
    private String makeUpdateValuesStr(GoodsExcelVO goods, final String createdBy, final String createdName, final LocalDateTime nowTime, final String status) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        // id
        data.append(goods.getId()).append(", ");
        // code
        data.append("'").append(goods.getCode()).append("',");
        // party_group_code
        data.append("'").append(UserUtil.getBranchCode()).append("', ");
        // shipment_code
        data.append("'").append(goods.getShipmentCode()).append("',");
        // shipment_name
        data.append("'").append(goods.getShipmentName()).append("',");
        // type_code
        data.append("'").append(goods.getGoodsTypeCode()).append("',");
        // type_name
        data.append("'").append(goods.getTypeName()).append("',");
        // good_code
        data.append("'").append(goods.getGoodCode()).append("',");
        // bar_code
        data.append("'").append(goods.getGoodsBarCode()).append("',");
        // name
        data.append("'").append(goods.getGoodsName()).append("',");
        // unit_code
        data.append("'").append(goods.getUnitCode()).append("',");
        // unit_precision
        data.append("'").append(goods.getUnitPrecision()).append("',");
        // spec
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getSpec(), StrUtil.EMPTY)).append("',");
        // lot_number
        data.append("'").append(goods.getLotNumber()).append("',");
        // type
        data.append("'").append(goods.getType()).append("',");
        // sale
        data.append("'").append(goods.getSale()).append("',");
        // purchase
        data.append("'").append(goods.getPurchase()).append("',");
        // quality
        data.append("'").append(NUM_ONE).append("',");
        // storage_strategy
        data.append("'").append(goods.getStorageStrategy()).append("',");
        // early_warning
        data.append(goods.getEarlyWarning()).append(",");
        // receiving_warning
        data.append(goods.getReceivingWarning()).append(",");
        // send_warning
        data.append(goods.getSendWarning()).append(",");
        // brand_code
        data.append("'").append(ObjectUtil.defaultIfNull(goods.getBrandCode(), StrUtil.EMPTY)).append("',");
        // remark
        data.append("'").append(goods.getRemarks()).append("',");
        // status
        data.append("'").append(status).append("',");
        // is_deleted
        data.append("'").append(NUM_ZERO).append("',");
        // created_by
        data.append("'").append(goods.getCreatedBy()).append("',");
        // gmt_created
        data.append("'").append(goods.getGmtCreated()).append("',");
        // modified_by
        data.append("'").append(createdBy).append("',");
        // gmt_modified
        data.append("'").append(nowTime).append("',");
        // created_name
        data.append("'").append(goods.getCreatedName()).append("',");
        // modified_name
        data.append("'").append(createdName).append("'");

        data.append(")");

        return  data.toString();
    }

    /**
     * copy字段给vo
     * @param goods
     * @param vo
     */
    public void copyUpdateProperties(Goods goods, GoodsExcelVO vo){
        vo.setId(goods.getId());
        vo.setCode(goods.getCode());
        vo.setStatus(goods.getStatus());
        vo.setGmtCreated(goods.getGmtCreated());
        vo.setCreatedBy(goods.getCreatedBy());
        vo.setCreatedName(goods.getCreatedName());
    }


    /**
     * 获取涉及到的货主下的所有商品信息，key为“货主code-商品serialNumber”，用于快速商品信息是否存在
     * @param tempData
     * @return
     */
    private Map<String, Goods> getImportShipmentGoodsMap(List<GoodsExcelVO> tempData) {
        Map<String, List<GoodsExcelVO>> shipmentCodeMap = tempData.stream().collect(Collectors.groupingBy(e->e.getShipmentCode()));
        List<Goods> goodsList = this.baseMapper.selectList(new LambdaQueryWrapper<Goods>().in(Goods::getShipmentCode, shipmentCodeMap.keySet()).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
        Map<String, Goods> goodsMap = new HashMap<>();
        if (IterUtil.isNotEmpty(goodsList)) {
            for (Goods goods : goodsList) {
                goodsMap.put(goods.getShipmentCode() + "-" + goods.getGoodCode(), goods);
            }
        }
        return goodsMap;
    }

    @Override
    public int countByTypeCode(String typeCode) {
        return this.count(new QueryWrapper<Goods>().lambda().eq(Goods::getTypeCode, typeCode).eq(Goods::getDeleted, NUM_ZERO).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 根据商品code，拼装商品信息
     *
     * @param code
     * @return
     */
    @Override
    public Map<String, Object> makeGoods(String code) {
        return goodsCache.get(code);
    }


    /**
     * 模糊查询商品
     * @param goodCode 商品编码
     * @param goodsName 商品名称
     */
    @Override
    public List<Map<String,Object>> searchByGoodCodeAndName(String goodCode, String goodsName,String groupCode,List<String> shipmentCodes){
        List<String> goodsCodes = baseMapper.searchByGoodCodeAndName(goodCode,goodsName,groupCode,shipmentCodes);
        return goodsCodes.parallelStream().map(s->goodsCache.get(s)).collect(Collectors.toList());
    }
}
